//
// RS Game Framework
// Copyright © 2009 Jedd Haberstro
// jhaberstro@gmail.com
// 
// $Id:
//

#ifndef RS_THREADLOCALPTR_HPP
#define RS_THREADLOCALPTR_HPP

#include "rs/Portability.hpp"

namespace rs
{
    namespace internal
    {   
        void SetTSSData(void*& key, void (*cleanup)(void*), void* data, Boolean destroy);
        
        void* GetTSSData(void const* key);
        
        template< typename T >
        void DefaultCleanupFunction(void* data) {
            delete static_cast< T* >(data);
        }
    }
    
    template< typename T >
    class ThreadLocalPtr
    {
    public:
        
        typedef T ValueType;
        typedef T& Reference;
        typedef T const& ConstReference;
        typedef T* Pointer;
        typedef T const* ConstPointer;
                
        typedef void(*CleanupFunction)(void*);
        
    public:
        
        ThreadLocalPtr();
        
        explicit ThreadLocalPtr(CleanupFunction cleanup);
        
        ~ThreadLocalPtr();
        
        Pointer Get() const;
        
        Pointer operator->() const;
        
        Reference operator*() const;
        
        Pointer Release();
        
        void Reset(Pointer value = 0);
        
    private:
                
        ThreadLocalPtr(ThreadLocalPtr const& other);
        
        ThreadLocalPtr& operator=(ThreadLocalPtr const& other);
        
    private:
        
        void* key_;
        CleanupFunction cleanup_;
    };
    
    template< typename T >
    inline ThreadLocalPtr< T >::ThreadLocalPtr()
    : cleanup_(&(internal::DefaultCleanupFunction< ValueType >)) {
    }
    
    template< typename T >
    inline ThreadLocalPtr< T >::ThreadLocalPtr(CleanupFunction cleanup)
    : cleanup_(cleanup) {
    } 
    
    template< typename T >
    inline ThreadLocalPtr< T >::~ThreadLocalPtr() {
        Reset();
    }
    
    template< typename T >
    inline typename ThreadLocalPtr< T >::Pointer ThreadLocalPtr< T >::Get() const {
        return static_cast< Pointer >(internal::GetTSSData(key_));
    }
    
    template< typename T >
    inline typename ThreadLocalPtr< T >::Pointer ThreadLocalPtr< T >::operator->() const {
        return Get();
    }
    
    template< typename T >
    inline typename ThreadLocalPtr< T >::Reference ThreadLocalPtr< T >::operator*() const {
        return *Get();
    }
    
    template< typename T >
    inline typename ThreadLocalPtr< T >::Pointer ThreadLocalPtr< T >::Release() {
        ConstPointer result = Get();
        internal::SetTSSData(key_, cleanup_, 0, false);
        return result;
    }
    
    template< typename T >
    inline void ThreadLocalPtr< T >::Reset(Pointer value) {
        if (Get() != value) {
            internal::SetTSSData(key_, cleanup_, value, true);
        }
    }
}

#endif // RS_THREADLOCALPTR_HPP